﻿#include <cassert>
#include <string>
#include <sstream>
#include <fstream>
#include <windows.h>
#include "character_encoding_test.h"

USING_NS_TEST;
USING_NS_STD;

elloop_test::EncodingTest::EncodingTest() {

}

elloop_test::EncodingTest::~EncodingTest() {

}

EncodingTest* elloop_test::EncodingTest::run() {
	//std::cout.setf(std::ios::boolalpha);
	//psln(containEmoji());
	//// if i don't unsetf here, all codes following will be boolalpha.
	//std::cout.unsetf(std::ios::boolalpha);

	//char c = -1;
	//if (c >= 255) {
	//	// default char is unsigned.
	//}
	//else {
	//	//default char is signed.
	//}

    //printWString();
	
    //printString();

    //testUtf8ToUnicode();

    //testReadingHexString();


    

    //using_the_unicode_to_utf8();
    testUtf8ToUnicode();

	return this;
}

bool elloop_test::EncodingTest::containEmoji() {
	return false;
}

void elloop_test::EncodingTest::printWString()
{
    wstring s(L"😁");
    auto len2 = s.length();
    size_t s_size = sizeof (s);

    auto len = s.size();
    wchar_t * s_ptr = &(*s.begin());

    size_t char_size = sizeof (*s_ptr);

    for (auto i = 0; i < len; ++i) {
        int c = *(s_ptr + i);
        psln(c);
    }
}

void elloop_test::EncodingTest::printString()
{
    string s("😁");
    auto len2 = s.length();
    size_t s_size = sizeof (s);

    auto len = s.size();
    char * s_ptr = &(*s.begin());

    size_t char_size = sizeof (*s_ptr);

    for (auto i = 0; i < len; ++i) {
        int c = *(s_ptr + i);
        psln(c);
    }
}

unsigned int elloop_test::EncodingTest::utf8_to_unicode(const std::vector<unsigned char> & utf8)
{
    assert(utf8.size() > 0);
    unsigned int unicode_result(0);

    size_t num_of_bytes = utf8[0] & 0xf0;
    switch (num_of_bytes)
    {
    case 0x00:
        num_of_bytes = 1;
        unicode_result = utf8[0];
        break;
    case 0xc0:
        num_of_bytes = 2;
        unicode_result += (utf8[1] & 0x3f);
        unicode_result += (utf8[0] & 0x1f) << 6;
        break;
    case 0xe0:
        num_of_bytes = 3;
        unicode_result += (utf8[2] & 0x3f);
        unicode_result += (utf8[1] & 0x3f) << 6;
        unicode_result += (utf8[0] & 0x0f) << 12;
        break;
    case 0xf0:
        num_of_bytes = 4;
        unicode_result += (utf8[3] & 0x3f);
        unicode_result += (utf8[2] & 0x3f) << 6;
        unicode_result += (utf8[1] & 0x3f) << 12;
        unicode_result += (utf8[0] & 0x07) << 18;
        break;
    default:
        break;
    }

    return unicode_result;
}

void elloop_test::EncodingTest::testUtf8ToUnicode()
{
    /*u8.push_back(0xf0);
    u8.push_back(0x9f);
    u8.push_back(0x89);
    u8.push_back(0x91);
    
    unsigned int uni = utf8_to_unicode(u8);

    cout.setf(ios::hex);
    cout << "unicode: " << hex << uni << endl;*/
    /* string line("");
     istringstream ss;
     while (getline(cin, line)) {
     u8.clear();

     ss.str(line);
     string bytes;
     unsigned char bytet;
     while (ss >> bytes) {
     bytet = convert_to_int(bytes);
     u8.push_back(bytet);
     }

     p("utf8:");
     for (int i = 0; i < u8.size(); ++i) {
     cout << hex << u8[i] << " ";
     }
     cr;

     unsigned int uni = utf8_to_unicode(u8);

     cout.setf(ios::hex);
     cout << "unicode: " << hex << uni << endl;
     }*/
    //psln(uni);

    vector<unsigned char> u8;

    string h, l, q, k;
    while (cin >> h >> l >> q) {
        u8.clear();
        unsigned char b1 = convert_to_int(h);
        unsigned char b2 = convert_to_int(l);
        unsigned char b3 = convert_to_int(q);
        //unsigned char b4 = convert_to_int(k);
        u8.push_back(b1);
        u8.push_back(b2);
        u8.push_back(b3);
        //u8.push_back(b4);


        unsigned int uni = utf8_to_unicode(u8);

        cout.setf(ios::hex);
        cout << "utf8: " << hex << (int)b1 << " " << hex << (int)b2 << " "  << (int)b3 << endl;
        cout << "unicode: " << hex << uni << endl;
    }
}

unsigned char elloop_test::EncodingTest::convert_to_int(const string & bytes)
{
    unsigned char result(0);
    if (bytes[0] >= 'a') {
        result += (bytes[0] - 'a' + 10) * 16;
    }
    else {
        result += (bytes[0] - '0') * 16;
    }

    if (bytes[1] >= 'a') {
        result += (bytes[1] - 'a' + 10);
    }
    else {
        result += bytes[1] - '0';
    }

    return result;

}

void elloop_test::EncodingTest::testReadingHexString()
{
    //ifstream is;
    //is.open("d:\\LimitedUnicodes.txt", ios::in);

    //string line("");
    //string unicode("");
    ////unsigned int unicode(0);
    //if (is.is_open()) {
    //    while (getline(is, line)) {
    //        istringstream iss(line);
    //        while (iss >> unicode)
    //        {
    //            cout << "unicode: " << unicode << endl;
    //            cout << "int: " << hexstring_to_int(unicode) << endl;
    //        }
    //    }
    //    is.close();
    //}
    int i = (1 << 4);
    psln(i);

    string sss("");
    while (cin >> sss) {
        cout << sss << endl;
        int tt = hexstring_to_int(sss);
        cout.unsetf(ios::hex);
        cout << "int: " << tt << endl;
        cout << hex << "hex: " << tt << endl;
    }
}

unsigned int elloop_test::EncodingTest::hexstring_to_int(const std::string & str)
{
    unsigned int result(0);
    string::size_type num_length = str.size();
    for (string::size_type i = 0; i < num_length; ++i) {
        result += (hexChar_to_int(str[i]) << ((num_length - i - 1) * 4));
    }
    return result;
}

int elloop_test::EncodingTest::hexChar_to_int(char hex_char)
{
    if (hex_char > '9') {
        return (hex_char - 'a' + 10);
    }
    else {
        return (hex_char - '0');
    }
}

const std::vector<unsigned char> elloop_test::EncodingTest::unicode_to_utf8(unsigned int unicode)
{
    throw exception("no implementation of this method");
}

const std::vector<unsigned char> elloop_test::EncodingTest::unicode_to_utf8(const std::string & hex_string)
{
    // TODO: regx to check if hex_string is format like 'U+ffef'
    string first_byte_str = hex_string.substr(2, 2);
    unsigned char first_byte = hexstring_to_int(first_byte_str);

    string second_byte_str = hex_string.substr(4, 2);
    unsigned char second_byte = hexstring_to_int(second_byte_str);

    vector<unsigned char> result;

    unsigned int unicode_value = (first_byte & 0xf0) * 256 + (first_byte & 0x0f) * 256 + second_byte;

    if (unicode_value < 0x800 && unicode_value > 0x7f) {
        // two bytes

    }
    else if (unicode_value > 0x7ff && unicode_value < 0x10000) {
        // three bytes
        result.push_back(0xe0 + ((first_byte & 0xf0) >> 4));
        result.push_back(0x80 + ((first_byte & 0x0f) << 2) + ((second_byte & 0xc0) >> 6));
        result.push_back(0x80 + (second_byte & 0x3f));
    }
    else if (unicode_value > 0xffff) {
        // four bytes
    }

    return result;
}

std::string elloop_test::EncodingTest::utf8_to_hex_string(const std::vector<unsigned char>& utf8)
{
    string result("");
    for (int i = 0; i < utf8.size(); ++i) {
        unsigned char t_byte = utf8[i];
        string hex_str = convert_byte_to_hex_string(t_byte);
        result += hex_str;
    }
    return result;
}

std::string elloop_test::EncodingTest::convert_byte_to_hex_string(unsigned char val)
{
    unsigned char low_bit = val - 16 * (val / 16);
    low_bit = low_bit < 10 ? '0' + low_bit : 'a' + low_bit - 10;
    unsigned char hight_bit = val / 16;
    hight_bit = hight_bit < 10 ? '0' + hight_bit : 'a' + hight_bit - 10;
    string s;
    s += hight_bit;
    s += low_bit;
    return s;
}

void elloop_test::EncodingTest::using_the_unicode_to_utf8()
{
    string s;
    while (cin >> s) {

        const vector<unsigned char> utf8_code = unicode_to_utf8(s);

        string hex_string = utf8_to_hex_string(utf8_code);

        cout << hex_string << endl;
    }
    
}

//char convert_to_hex()
// todo: convert_decimal_to_hex_string impl.



